Skip to content

feat(davinci-client): add QRCode collector support#562

Open
ryanbas21 wants to merge 2 commits intomainfrom
qr-code
Open

feat(davinci-client): add QRCode collector support#562
ryanbas21 wants to merge 2 commits intomainfrom
qr-code

Conversation

@ryanbas21
Copy link
Copy Markdown
Collaborator

@ryanbas21 ryanbas21 commented Mar 30, 2026

Jira

https://pingidentity.atlassian.net/browse/SDKS-4686

Summary

  • Adds QR_CODE field type support to the DaVinci client SDK, enabling applications to render QR codes returned by DaVinci flows
  • Introduces QrCodeCollector (NoValueCollector family) with output.src (base64 data URI) and output.fallbackText (manual pairing key)
  • Wires QR_CODE field dispatch in the node reducer with defensive fallbacks for malformed server responses
  • Adds QR code rendering component to the sample davinci-app

Changes

davinci-client

  • davinci.types.ts — New QrCodeField type (type: 'QR_CODE', key, content, fallbackText?)
  • collector.types.ts — New QrCodeCollectorBase interface with src and fallbackText in output
  • collector.utils.tsreturnQrCodeCollector factory with defensive fallbacks matching returnNoValueCollector pattern
  • node.reducer.tsQR_CODE early return before switch (same pattern as LABEL)
  • node.types.ts / types.tsQrCodeCollector registered in Collectors union and public API

davinci-app (sample)

  • components/qr-code.ts — Renders QR code <img> with fallback text, checks collector.error
  • main.ts — Wires QrCodeCollector branch into render loop
  • server-configs.ts — Adds QR code flow test config

Tests

  • 5 unit tests for returnQrCodeCollector (happy path, missing fallbackText, missing content, missing key, accumulated errors)
  • 1 reducer integration test for QR_CODE field dispatch
  • Type-level test updates for Collectors union

Test plan

  • 232 tests pass (nx run @forgerock/davinci-client:nxTest)
  • Typecheck clean for davinci-client and davinci-app
  • App builds successfully
  • Manual: navigate to http://localhost:5829/?clientId=c12743f9-08e8-4420-a624-71bbb08e9fe1 and verify QR code renders
Screenshot 2026-03-30 at 1 36 39 PM

Summary by CodeRabbit

  • New Features

    • QR code support: displays QR code images with optional manual code text and integrates into the collector flow.
  • Tests

    • Added unit, type-level, integration, and end-to-end tests covering QR code creation, error handling, reducer behavior, and UI rendering.
  • Chores

    • Updated server client configuration, CI command usage, and added a changeset for release.

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 30, 2026

🦋 Changeset detected

Latest commit: aa3c81f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 12 packages
Name Type
@forgerock/davinci-client Minor
@forgerock/device-client Minor
@forgerock/journey-client Minor
@forgerock/oidc-client Minor
@forgerock/protect Minor
@forgerock/sdk-types Minor
@forgerock/sdk-utilities Minor
@forgerock/iframe-manager Minor
@forgerock/sdk-logger Minor
@forgerock/sdk-oidc Minor
@forgerock/sdk-request-middleware Minor
@forgerock/storage Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 30, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds QR code collector support: new QR field/collector types and utils, reducer integration, UI component and e2e test for rendering QR images/fallbacks, server-config updates, CI tweaks, and a changeset for releasing davinci-client.

Changes

Cohort / File(s) Summary
Types & Public Exports
packages/davinci-client/src/lib/collector.types.ts, packages/davinci-client/src/lib/davinci.types.ts, packages/davinci-client/src/lib/node.types.ts, packages/davinci-client/src/types.ts
Added QrCodeField/QrCodeCollector types; extended unions (ReadOnlyFields, NoValueCollectorTypes, NoValueCollectors, Collectors) and re-exported collector alias.
Collector Utilities & Tests
packages/davinci-client/src/lib/collector.utils.ts, packages/davinci-client/src/lib/collector.utils.test.ts
Added returnQrCodeCollector, broadened returnNoValueCollector generic constraint to accept QrCodeField; tests for success, defaults, and error cases.
Reducer & Node Tests
packages/davinci-client/src/lib/node.reducer.ts, packages/davinci-client/src/lib/node.reducer.test.ts, packages/davinci-client/src/lib/node.types.test-d.ts
Reducer mapping updated to produce QR-code collectors for QR_CODE fields; state typing updated; unit/type tests added.
E2E App UI
e2e/davinci-app/components/qr-code.ts, e2e/davinci-app/main.ts
New QR component that appends an <img> (collector.output.src) and optional fallback or error text to forms; main integrates this component into collector render flow.
E2E Server Configs
e2e/davinci-app/server-configs.ts
Adjusted scopes for an existing client (removed revoke), and added a new Ping client entry with openid profile email and Ping well-known URL.
E2E Test
e2e/davinci-suites/src/qr-code.test.ts
New Playwright test that drives device registration flow, asserts QR image appears with data:image/png;base64, src and conditionally checks fallback text.
CI & Release Metadata
.github/workflows/ci.yml, .changeset/fast-ways-rest.md
CI command invocation changed from npx nx-cloud fix-ci to pnpm nx fix-ci and minor quoting tweaks; added changeset declaring a minor release for davinci-client (QR code collector support).

Sequence Diagram(s)

sequenceDiagram
  participant Field as DaVinci Field (QR_CODE)
  participant Reducer as Node Reducer
  participant Collector as returnQrCodeCollector
  participant UI as e2e QR Component
  participant Form as HTMLFormElement

  Field->>Reducer: dispatch node/next with field
  Reducer->>Collector: returnQrCodeCollector(field, idx)
  Collector-->>Reducer: QrCodeCollector (output, error)
  Reducer-->>UI: renderer receives collector
  UI->>Form: append image (collector.output.src) or error message and optional fallback text
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • cerebrl
  • ancheetah
  • SteinGabriel

Poem

I nibble pixels, stitch the QR,
I paste a pic where forms are.
If bits won't render, fallback sings,
I leave a code for human wings,
A hopping Rabbit ships the dots 🐇

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(davinci-client): add QRCode collector support' is concise, uses conventional commit format, and directly summarizes the main change: adding QRCode collector support to davinci-client.
Description check ✅ Passed The PR description is comprehensive and well-structured. It includes a JIRA ticket link, a clear summary of changes, organized sections for davinci-client and davinci-app modifications, test coverage details, and a test plan with verification evidence.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch qr-code

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@nx-cloud
Copy link
Copy Markdown
Contributor

nx-cloud bot commented Mar 30, 2026

View your CI Pipeline Execution ↗ for commit aa3c81f

Command Status Duration Result
nx run-many -t build --no-agents ✅ Succeeded <1s View ↗
nx affected -t build lint test typecheck e2e-ci ✅ Succeeded 7m 16s View ↗

☁️ Nx Cloud last updated this comment at 2026-04-06 16:13:57 UTC

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Mar 30, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 14.90%. Comparing base (5d6747a) to head (aa3c81f).
⚠️ Report is 16 commits behind head on main.

❌ Your project status has failed because the head coverage (14.90%) is below the target coverage (40.00%). You can increase the head coverage or adjust the target coverage.

Additional details and impacted files
@@             Coverage Diff             @@
##             main     #562       +/-   ##
===========================================
- Coverage   70.90%   14.90%   -56.00%     
===========================================
  Files          53      153      +100     
  Lines        2021    26304    +24283     
  Branches      377     1071      +694     
===========================================
+ Hits         1433     3921     +2488     
- Misses        588    22383    +21795     
Files with missing lines Coverage Δ
packages/davinci-client/src/lib/collector.types.ts 100.00% <ø> (ø)
packages/davinci-client/src/lib/collector.utils.ts 81.35% <100.00%> (ø)
packages/davinci-client/src/lib/davinci.types.ts 100.00% <ø> (ø)
packages/davinci-client/src/lib/node.reducer.ts 67.93% <100.00%> (ø)
packages/davinci-client/src/lib/node.types.ts 100.00% <ø> (ø)
packages/davinci-client/src/types.ts 50.00% <ø> (ø)

... and 95 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Mar 30, 2026

Open in StackBlitz

@forgerock/davinci-client

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/davinci-client@562

@forgerock/device-client

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/device-client@562

@forgerock/journey-client

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/journey-client@562

@forgerock/oidc-client

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/oidc-client@562

@forgerock/protect

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/protect@562

@forgerock/sdk-types

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/sdk-types@562

@forgerock/sdk-utilities

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/sdk-utilities@562

@forgerock/iframe-manager

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/iframe-manager@562

@forgerock/sdk-logger

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/sdk-logger@562

@forgerock/sdk-oidc

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/sdk-oidc@562

@forgerock/sdk-request-middleware

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/sdk-request-middleware@562

@forgerock/storage

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/storage@562

commit: aa3c81f

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 30, 2026

Deployed 72aac18 to https://ForgeRock.github.io/ping-javascript-sdk/pr-562/72aac181b67c54d4ecc86b1701222e573850f394 branch gh-pages in ForgeRock/ping-javascript-sdk

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 30, 2026

📦 Bundle Size Analysis

📦 Bundle Size Analysis

🚨 Significant Changes

🔻 @forgerock/device-client - 0.0 KB (-9.7 KB, -100.0%)
🔻 @forgerock/journey-client - 0.0 KB (-87.8 KB, -100.0%)
🔺 @forgerock/journey-client - 89.2 KB (+1.4 KB, +1.6%)

📊 Minor Changes

📉 @forgerock/device-client - 9.7 KB (-0.0 KB)
📈 @forgerock/davinci-client - 41.6 KB (+0.4 KB)

➖ No Changes

@forgerock/sdk-utilities - 11.2 KB
@forgerock/sdk-request-middleware - 4.5 KB
@forgerock/iframe-manager - 2.4 KB
@forgerock/sdk-oidc - 4.8 KB
@forgerock/storage - 1.5 KB
@forgerock/sdk-logger - 1.6 KB
@forgerock/oidc-client - 24.9 KB
@forgerock/protect - 150.1 KB
@forgerock/sdk-types - 7.9 KB


14 packages analyzed • Baseline from latest main build

Legend

🆕 New package
🔺 Size increased
🔻 Size decreased
➖ No change

ℹ️ How bundle sizes are calculated
  • Current Size: Total gzipped size of all files in the package's dist directory
  • Baseline: Comparison against the latest build from the main branch
  • Files included: All build outputs except source maps and TypeScript build cache
  • Exclusions: .map, .tsbuildinfo, and .d.ts.map files

🔄 Updated automatically on each push to this PR

@ryanbas21 ryanbas21 force-pushed the qr-code branch 2 times, most recently from a2633f1 to 65a6b01 Compare March 30, 2026 19:58
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
e2e/davinci-app/components/qr-code.ts (1)

10-14: Consider adding a data-testid to the error element for e2e test coverage.

This would enable testing error scenarios in automated tests.

🧪 Proposed improvement
   if (collector.error) {
     const errorEl = document.createElement('p');
     errorEl.innerText = `QR Code error: ${collector.error}`;
+    errorEl.setAttribute('data-testid', 'qr-code-error');
     formEl.appendChild(errorEl);
     return;
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/davinci-app/components/qr-code.ts` around lines 10 - 14, Add a
data-testid attribute to the error paragraph so e2e tests can select it: when
handling collector.error in the QR code component (the branch that creates
errorEl and appends to formEl), set errorEl.dataset.testid = 'qr-code-error' (or
use errorEl.setAttribute('data-testid', 'qr-code-error')) before appending; keep
the existing innerText and return behavior unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@e2e/davinci-app/components/qr-code.ts`:
- Around line 10-14: Add a data-testid attribute to the error paragraph so e2e
tests can select it: when handling collector.error in the QR code component (the
branch that creates errorEl and appends to formEl), set errorEl.dataset.testid =
'qr-code-error' (or use errorEl.setAttribute('data-testid', 'qr-code-error'))
before appending; keep the existing innerText and return behavior unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 554b1372-8058-424c-9dda-d435ac4bab4a

📥 Commits

Reviewing files that changed from the base of the PR and between 67c2191 and 16433eb.

📒 Files selected for processing (12)
  • e2e/davinci-app/components/qr-code.ts
  • e2e/davinci-app/main.ts
  • e2e/davinci-app/server-configs.ts
  • packages/davinci-client/src/lib/collector.types.ts
  • packages/davinci-client/src/lib/collector.utils.test.ts
  • packages/davinci-client/src/lib/collector.utils.ts
  • packages/davinci-client/src/lib/davinci.types.ts
  • packages/davinci-client/src/lib/node.reducer.test.ts
  • packages/davinci-client/src/lib/node.reducer.ts
  • packages/davinci-client/src/lib/node.types.test-d.ts
  • packages/davinci-client/src/lib/node.types.ts
  • packages/davinci-client/src/types.ts

nx-cloud[bot]

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor

@nx-cloud nx-cloud bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nx Cloud has identified a flaky task in your failed CI:

🔂 Since the failure was identified as flaky, we triggered a CI rerun by adding an empty commit to this branch.

Nx Cloud View detailed reasoning in Nx Cloud ↗

🔔 Heads up, your workspace has pending recommendations ↗ to auto-apply fixes for similar failures.


🎓 Learn more about Self-Healing CI on nx.dev

* @param {number} idx - The index to be used in the id of the QrCodeCollector.
* @returns {QrCodeCollectorBase} The constructed QrCodeCollector object.
*/
export function returnQrCodeCollector(field: QrCodeField, idx: number): QrCodeCollectorBase {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we reuse the generic returnNoValueCollector function? I feel like we can change around the properties a bit to make it fit. It feels like fallbackText should be the label and we just need to introduce a new prop for the src.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
e2e/davinci-app/components/qr-code.ts (1)

20-23: Validate QR image payload format before assigning img.src.

Line 20 trusts collector.output.src directly; add a data-URI guard so malformed/hosted URLs are rejected explicitly.

Proposed hardening
+  const isDataImageUri = /^data:image\/[a-zA-Z+.-]+;base64,/.test(collector.output.src);
+  if (!isDataImageUri) {
+    const errorEl = document.createElement('p');
+    errorEl.innerText = 'QR Code error: invalid QR image payload';
+    formEl.appendChild(errorEl);
+    return;
+  }
+
   const img = document.createElement('img');
   img.src = collector.output.src;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/davinci-app/components/qr-code.ts` around lines 20 - 23, Validate that
collector.output.src is a proper data URI before assigning it to img.src: in the
QR component where img.src = collector.output.src is set, add a guard that
checks the string starts with "data:image/" and matches a data URI pattern
(e.g., /^data:image\/[a-zA-Z]+;base64,/) and only then assign to img.src; if
validation fails, do not set img.src and instead set a safe fallback (e.g.,
leave alt text, set a placeholder src or log/emit an error) prior to
container.appendChild(img) so malformed or hosted URLs are explicitly rejected.
e2e/davinci-suites/src/qr-code.test.ts (1)

21-21: Replace generic requestfinished waits with state-specific assertions.

Lines 21, 26, 30, and 35 can be flaky because any request completion may satisfy them. Wait for the next expected UI state (or specific response URL) after each action.

Also applies to: 26-26, 30-30, 35-35

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/davinci-suites/src/qr-code.test.ts` at line 21, Replace the flaky
page.waitForEvent('requestfinished') calls with state-specific waits: after each
action that currently uses page.waitForEvent('requestfinished') (instances of
page.waitForEvent('requestfinished') in the test), wait for the specific network
response using page.waitForResponse(resp =>
resp.url().includes('<expected-path-or-endpoint>') && resp.status() === 200) or
wait for the expected UI state via await
page.waitForSelector('<expected-selector>', { state: 'visible' }) (choose the
response URL or selector that matches the next UI change for each of the four
occurrences).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@e2e/davinci-app/components/qr-code.ts`:
- Around line 20-23: Validate that collector.output.src is a proper data URI
before assigning it to img.src: in the QR component where img.src =
collector.output.src is set, add a guard that checks the string starts with
"data:image/" and matches a data URI pattern (e.g.,
/^data:image\/[a-zA-Z]+;base64,/) and only then assign to img.src; if validation
fails, do not set img.src and instead set a safe fallback (e.g., leave alt text,
set a placeholder src or log/emit an error) prior to container.appendChild(img)
so malformed or hosted URLs are explicitly rejected.

In `@e2e/davinci-suites/src/qr-code.test.ts`:
- Line 21: Replace the flaky page.waitForEvent('requestfinished') calls with
state-specific waits: after each action that currently uses
page.waitForEvent('requestfinished') (instances of
page.waitForEvent('requestfinished') in the test), wait for the specific network
response using page.waitForResponse(resp =>
resp.url().includes('<expected-path-or-endpoint>') && resp.status() === 200) or
wait for the expected UI state via await
page.waitForSelector('<expected-selector>', { state: 'visible' }) (choose the
response URL or selector that matches the next UI change for each of the four
occurrences).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1480b882-5109-4835-be79-54a3386905ba

📥 Commits

Reviewing files that changed from the base of the PR and between eb68c9c and 891b136.

📒 Files selected for processing (14)
  • .changeset/fast-ways-rest.md
  • e2e/davinci-app/components/qr-code.ts
  • e2e/davinci-app/main.ts
  • e2e/davinci-app/server-configs.ts
  • e2e/davinci-suites/src/qr-code.test.ts
  • packages/davinci-client/src/lib/collector.types.ts
  • packages/davinci-client/src/lib/collector.utils.test.ts
  • packages/davinci-client/src/lib/collector.utils.ts
  • packages/davinci-client/src/lib/davinci.types.ts
  • packages/davinci-client/src/lib/node.reducer.test.ts
  • packages/davinci-client/src/lib/node.reducer.ts
  • packages/davinci-client/src/lib/node.types.test-d.ts
  • packages/davinci-client/src/lib/node.types.ts
  • packages/davinci-client/src/types.ts
✅ Files skipped from review due to trivial changes (5)
  • .changeset/fast-ways-rest.md
  • packages/davinci-client/src/types.ts
  • packages/davinci-client/src/lib/node.types.test-d.ts
  • packages/davinci-client/src/lib/collector.utils.test.ts
  • packages/davinci-client/src/lib/node.types.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • e2e/davinci-app/main.ts
  • packages/davinci-client/src/lib/node.reducer.ts
  • packages/davinci-client/src/lib/collector.utils.ts
  • packages/davinci-client/src/lib/node.reducer.test.ts
  • packages/davinci-client/src/lib/collector.types.ts

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
e2e/davinci-suites/src/qr-code.test.ts (2)

47-52: Conditional fallback assertion may silently pass if fallback is missing.

The conditional if (fallbackVisible) means the test will pass even if the fallback element is never rendered. If the fallback text is expected behavior for this flow (the PR screenshot shows "04AMFGOEWFENIB62O" as a manual code), consider making this assertion unconditional:

  const fallback = page.locator('[data-testid="qr-code-fallback"]');
- const fallbackVisible = await fallback.isVisible();
- if (fallbackVisible) {
-   const fallbackText = await fallback.textContent();
-   expect(fallbackText).toBeTruthy();
- }
+ await expect(fallback).toBeVisible();
+ const fallbackText = await fallback.textContent();
+ expect(fallbackText).toBeTruthy();

If the fallback is truly optional depending on the flow configuration, consider adding a comment explaining when it's expected vs. optional.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/davinci-suites/src/qr-code.test.ts` around lines 47 - 52, The current
conditional check around the QR fallback allows the test to silently pass when
the fallback element is not present; update the test so that the fallback text
assertion is unconditional (remove the `if (fallbackVisible)` guard) and
directly assert the fallback element is visible and has non-empty text using the
existing `fallback`, `fallbackVisible`/`fallbackText` variables (e.g., ensure
you first assert `fallback.isVisible()` then `fallback.textContent()` is
truthy), or if the fallback is legitimately optional add a clear comment
explaining when it is expected and keep the conditional but assert visibility
state explicitly.

20-26: Consider more targeted request waiting to reduce flakiness.

Using page.waitForEvent('requestfinished') will resolve on any completed network request, not necessarily the one triggered by the preceding action. If the page has background requests (analytics, polling, etc.), this could introduce intermittent failures or false positives.

Consider using page.waitForResponse() with a URL pattern, or wrapping the click + wait in Promise.all:

await Promise.all([
  page.waitForResponse((resp) => resp.url().includes('/expected-endpoint') && resp.ok()),
  page.getByRole('button', { name: 'USER_LOGIN' }).click(),
]);

This pattern is more resilient and explicitly ties the wait to the expected request.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/davinci-suites/src/qr-code.test.ts` around lines 20 - 26, Replace the
broad page.waitForEvent('requestfinished') calls around the login actions with
targeted waits tied to the expected network request(s): wrap the click on
page.getByRole('button', { name: 'USER_LOGIN' }).click() and the Sign On click
in Promise.all paired with page.waitForResponse or page.waitForRequest using a
URL predicate that matches the login endpoint (e.g.,
resp.url().includes('/expected-endpoint') && resp.ok()), and do the same for the
second click on page.getByRole('button', { name: 'Sign On' }).click() so the
test explicitly waits for the correct request/response instead of any finished
network request.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@e2e/davinci-suites/src/qr-code.test.ts`:
- Around line 47-52: The current conditional check around the QR fallback allows
the test to silently pass when the fallback element is not present; update the
test so that the fallback text assertion is unconditional (remove the `if
(fallbackVisible)` guard) and directly assert the fallback element is visible
and has non-empty text using the existing `fallback`,
`fallbackVisible`/`fallbackText` variables (e.g., ensure you first assert
`fallback.isVisible()` then `fallback.textContent()` is truthy), or if the
fallback is legitimately optional add a clear comment explaining when it is
expected and keep the conditional but assert visibility state explicitly.
- Around line 20-26: Replace the broad page.waitForEvent('requestfinished')
calls around the login actions with targeted waits tied to the expected network
request(s): wrap the click on page.getByRole('button', { name: 'USER_LOGIN'
}).click() and the Sign On click in Promise.all paired with page.waitForResponse
or page.waitForRequest using a URL predicate that matches the login endpoint
(e.g., resp.url().includes('/expected-endpoint') && resp.ok()), and do the same
for the second click on page.getByRole('button', { name: 'Sign On' }).click() so
the test explicitly waits for the correct request/response instead of any
finished network request.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 22d283af-6079-4f32-983d-3fc1170d4352

📥 Commits

Reviewing files that changed from the base of the PR and between 891b136 and 806e69c.

📒 Files selected for processing (3)
  • .github/workflows/ci.yml
  • e2e/davinci-suites/src/qr-code.test.ts
  • packages/davinci-client/src/lib/collector.utils.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/davinci-client/src/lib/collector.utils.ts

@ryanbas21 ryanbas21 force-pushed the qr-code branch 2 times, most recently from d8af5c3 to 213bd20 Compare April 6, 2026 15:50
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@e2e/davinci-suites/src/qr-code.test.ts`:
- Around line 49-52: The current assertion uses fallbackVisible and
fallback.textContent() with toBeTruthy(), which allows responses like "Manual
code: " to pass; instead read the text via fallback.textContent(), remove the
known prefix (e.g., "Manual code:"), trim the remainder, and assert that the
trimmed string is non-empty (or matches /\S/). Update the test around the
fallbackVisible branch to compute const text = await fallback.textContent(),
strip the prefix and whitespace, then
expect(theStrippedText.length).toBeGreaterThan(0) (or
expect(theStrippedText).toMatch(/\S/)), referencing the existing fallbackVisible
and fallback variables.
- Around line 20-35: The unscoped page.waitForEvent('requestfinished') calls
after clicks (seen around page.getByRole(..., { name: 'USER_LOGIN' }),
page.getByRole(..., { name: 'Sign On' }), page.getByRole(..., { name:
'DEVICE_REGISTRATION' }), and page.getByRole(..., { name: 'Mobile App' })) are
racy; replace them with either (a) web-first UI assertions that auto-retry (e.g.
await expect(page.getByText('MFA Device Selection -
Registration')).toBeVisible() or expecting a navigation/URL change via
toHaveURL) or (b) scoped network waits that start before the action (use
Promise.all with page.waitForResponse(resp =>
resp.url().includes('<unique-path>') && resp.status() === 200) and the
.click()), matching the specific request path for the LOGIN, Sign On,
DEVICE_REGISTRATION and Mobile App actions instead of waiting for any finished
request.

In `@packages/davinci-client/src/lib/collector.utils.ts`:
- Around line 732-739: The returned object currently spreads base.output into
output, but base.output.label can be a non-string when server payloads are
malformed; update the output normalization in the return of the function (the
object that merges ...base and output: {...base.output, src: field.content ||
'', fallbackText: field.fallbackText || ''}) to explicitly normalize label to a
string (e.g., set label to base.output.label if typeof base.output.label ===
'string' else ''), so QrCodeCollectorBase's runtime shape guarantee is
preserved.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 86df6553-a839-4b3a-af53-15d5df2817de

📥 Commits

Reviewing files that changed from the base of the PR and between 806e69c and d8af5c3.

📒 Files selected for processing (3)
  • .github/workflows/ci.yml
  • e2e/davinci-suites/src/qr-code.test.ts
  • packages/davinci-client/src/lib/collector.utils.ts
✅ Files skipped from review due to trivial changes (1)
  • .github/workflows/ci.yml

Comment on lines +20 to +35
await page.getByRole('button', { name: 'USER_LOGIN' }).click();
await page.waitForEvent('requestfinished');

await page.getByLabel('Username').fill(username);
await page.getByLabel('Password').fill(password);
await page.getByRole('button', { name: 'Sign On' }).click();
await page.waitForEvent('requestfinished');

// Step 2: Select device registration
await page.getByRole('button', { name: 'DEVICE_REGISTRATION' }).click();
await page.waitForEvent('requestfinished');

// Step 3: Choose "Mobile App" from the device selection screen
await expect(page.getByText('MFA Device Selection - Registration')).toBeVisible();
await page.getByRole('button', { name: 'Mobile App' }).click();
await page.waitForEvent('requestfinished');
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In Playwright, is using page.waitForEvent('requestfinished') after a click considered flaky, and what are the recommended alternatives for deterministic synchronization in E2E tests?

💡 Result:

Yes—page.waitForEvent('requestfinished') after a click() is often flaky/racy in E2E tests because:

  • requestfinished fires for any request that completes (including redirects and even HTTP error responses like 404/503), so you can easily synchronize on the “wrong” request when multiple requests are in flight. [1]
  • If you start waiting after the click, you can miss fast network activity and then hang until timeout (classic race pattern).

Recommended deterministic alternatives

  1. Wait for a specific response (preferred when an API call is the real “done” signal)
    Register the wait before the click and match by URL/predicate:

    const respPromise = page.waitForResponse(r =>
      r.url().includes('/api/checkout') && r.status() === 200
    );
    await page.getByRole('button', { name: 'Buy' }).click();
    await respPromise;
  2. Wait for URL / navigation deterministically (preferred when the user-visible result is navigation)
    Use page.waitForURL() (Playwright explicitly recommends URL waiting patterns for navigation sync). [3]
    Typical race-safe pattern:

    await Promise.all([
      page.waitForURL('**/order/confirmation'),
      page.getByRole('button', { name: 'Buy' }).click(),
    ]);
  3. Wait on user-visible UI state via “web-first” assertions (preferred for true E2E behavior)
    Playwright recommends asserting what the user sees; these assertions auto-wait/retry until the condition is met. [2]

    await page.getByRole('button', { name: 'Buy' }).click();
    await expect(page.getByText('Order confirmed')).toBeVisible();
  4. If you must sync with network, target the request/response—not “any finished request”
    Prefer waitForResponse / waitForRequest with a predicate over requestfinished (which is broad and easy to mismatch). [1]

Source list

[1] Playwright docs: request event order and meaning of requestfinished (incl. redirects and HTTP error responses still ending in requestfinished). (playwright.dev)
[2] Playwright docs: Best Practices—test user-visible behavior; use web-first assertions that auto-wait. (playwright.dev)
[3] Playwright docs: Navigations—“Waiting for navigation” using page.waitForURL(). (playwright.dev)

Citations:


Unscoped requestfinished waits introduce race conditions and test flakiness.

On lines 21, 26, 30, and 35, page.waitForEvent('requestfinished') resolves on any finished request (including unrelated redirects and HTTP error responses). Starting the wait after the click creates a classic race: if the network activity completes before the listener is registered, the test will hang until timeout.

Replace with UI-state waits (web-first assertions that auto-retry) or scoped response waits matched by URL:

Stabilization pattern
-  await page.getByRole('button', { name: 'USER_LOGIN' }).click();
-  await page.waitForEvent('requestfinished');
+  await page.getByRole('button', { name: 'USER_LOGIN' }).click();
+  await expect(page.getByLabel('Username')).toBeVisible();

   await page.getByLabel('Username').fill(username);
   await page.getByLabel('Password').fill(password);
-  await page.getByRole('button', { name: 'Sign On' }).click();
-  await page.waitForEvent('requestfinished');
+  await page.getByRole('button', { name: 'Sign On' }).click();
+  await expect(page.getByRole('button', { name: 'DEVICE_REGISTRATION' })).toBeVisible();

-  await page.getByRole('button', { name: 'DEVICE_REGISTRATION' }).click();
-  await page.waitForEvent('requestfinished');
+  await page.getByRole('button', { name: 'DEVICE_REGISTRATION' }).click();
+  await expect(page.getByText('MFA Device Selection - Registration')).toBeVisible();

   await expect(page.getByText('MFA Device Selection - Registration')).toBeVisible();
-  await page.getByRole('button', { name: 'Mobile App' }).click();
-  await page.waitForEvent('requestfinished');
+  await page.getByRole('button', { name: 'Mobile App' }).click();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/davinci-suites/src/qr-code.test.ts` around lines 20 - 35, The unscoped
page.waitForEvent('requestfinished') calls after clicks (seen around
page.getByRole(..., { name: 'USER_LOGIN' }), page.getByRole(..., { name: 'Sign
On' }), page.getByRole(..., { name: 'DEVICE_REGISTRATION' }), and
page.getByRole(..., { name: 'Mobile App' })) are racy; replace them with either
(a) web-first UI assertions that auto-retry (e.g. await
expect(page.getByText('MFA Device Selection - Registration')).toBeVisible() or
expecting a navigation/URL change via toHaveURL) or (b) scoped network waits
that start before the action (use Promise.all with page.waitForResponse(resp =>
resp.url().includes('<unique-path>') && resp.status() === 200) and the
.click()), matching the specific request path for the LOGIN, Sign On,
DEVICE_REGISTRATION and Mobile App actions instead of waiting for any finished
request.

Comment on lines +49 to +52
if (fallbackVisible) {
const fallbackText = await fallback.textContent();
expect(fallbackText).toBeTruthy();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Strengthen fallback-text assertion to validate actual code content.

toBeTruthy() can still pass for "Manual code: " without a meaningful code. Consider asserting non-empty trimmed content after removing the prefix.

Suggested assertion tweak
   if (fallbackVisible) {
-    const fallbackText = await fallback.textContent();
-    expect(fallbackText).toBeTruthy();
+    const fallbackText = (await fallback.textContent()) ?? '';
+    const manualCode = fallbackText.replace(/^Manual code:\s*/i, '').trim();
+    expect(manualCode).not.toEqual('');
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/davinci-suites/src/qr-code.test.ts` around lines 49 - 52, The current
assertion uses fallbackVisible and fallback.textContent() with toBeTruthy(),
which allows responses like "Manual code: " to pass; instead read the text via
fallback.textContent(), remove the known prefix (e.g., "Manual code:"), trim the
remainder, and assert that the trimmed string is non-empty (or matches /\S/).
Update the test around the fallbackVisible branch to compute const text = await
fallback.textContent(), strip the prefix and whitespace, then
expect(theStrippedText.length).toBeGreaterThan(0) (or
expect(theStrippedText).toMatch(/\S/)), referencing the existing fallbackVisible
and fallback variables.

Comment on lines +732 to +739
return {
...base,
error: error || null,
output: {
...base.output,
src: field.content || '',
fallbackText: field.fallbackText || '',
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Normalize output.label for malformed QR payloads.

On Line 736, label is inherited from base.output and can be non-string at runtime when server payloads are malformed. This breaks the QrCodeCollectorBase runtime shape guarantee. Normalize it to '' the same way src/fallbackText are normalized.

Suggested patch
   return {
     ...base,
     error: error || null,
     output: {
       ...base.output,
+      label: typeof base.output.label === 'string' ? base.output.label : '',
       src: field.content || '',
       fallbackText: field.fallbackText || '',
     },
   };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return {
...base,
error: error || null,
output: {
...base.output,
src: field.content || '',
fallbackText: field.fallbackText || '',
},
return {
...base,
error: error || null,
output: {
...base.output,
label: typeof base.output.label === 'string' ? base.output.label : '',
src: field.content || '',
fallbackText: field.fallbackText || '',
},
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/davinci-client/src/lib/collector.utils.ts` around lines 732 - 739,
The returned object currently spreads base.output into output, but
base.output.label can be a non-string when server payloads are malformed; update
the output normalization in the return of the function (the object that merges
...base and output: {...base.output, src: field.content || '', fallbackText:
field.fallbackText || ''}) to explicitly normalize label to a string (e.g., set
label to base.output.label if typeof base.output.label === 'string' else ''), so
QrCodeCollectorBase's runtime shape guarantee is preserved.

Add QR_CODE field type support to the DaVinci client SDK,
enabling applications to render QR codes returned by DaVinci flows.

- Add QrCodeField type and QrCodeCollectorBase interface
- Add returnQrCodeCollector factory with defensive fallbacks
- Wire QR_CODE dispatch in node reducer (early return, same as LABEL)
- Export QrCodeCollector in public API types
- Add QR code component and config to sample davinci-app
- Add unit tests for factory and reducer integration
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants